﻿#if NETCORE

using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Builder;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.ResponseCompression;

namespace Apewer.Web
{

    /// <summary></summary>
    public abstract class AspNetCoreStartup
    {

        /// <summary>处理 WebSocket 请求。</summary>
        /// <remarks>默认值：FALSE。</remarks>
        public virtual bool UseWebSocket { get; protected set; } = false;

        /// <summary>WebSocket 保持活动的间隔时间，单位为秒。</summary>
        /// <remarks>默认值：10。</remarks>
        public virtual int KeepAlive { get; } = 10;

        /// <summary>处理请求。</summary>
        /// <param name="context"></param>
        public abstract void OnContext(HttpContext context);

        /// <summary>使用压缩。</summary>
        public virtual bool UseCompression { get => false; }

        /// <summary>处理 WebSocket 请求。</summary>
        public virtual void OnWebSocket(HttpContext context, System.Net.WebSockets.WebSocket webSocket) { }

        /// <summary>限制请求体的字节数，默认值：1073741824（1GB）。</summary>
        public virtual long MaxRequestBodySize { get => 1073741824L; }

        #region Runtime

        bool _usedWebSocket = false;
        IConfiguration _configuration;

        /// <summary></summary>
        public AspNetCoreStartup() { }

        /// <summary></summary>
        public AspNetCoreStartup(IConfiguration configuration) => _configuration = configuration;

        /// <summary>使用此方法添加服务到容器。</summary>
        /// <remarks>此方法由运行时调用。</remarks>
        public void ConfigureServices(IServiceCollection services)
        {
            var maxRequestBodySize = MaxRequestBodySize;
            if (maxRequestBodySize < 1L) maxRequestBodySize = 1073741824L;

            services.Configure<KestrelServerOptions>(options =>
            {
                // 同步 IO。
                options.AllowSynchronousIO = true;

                // 限制请求大小。
                options.Limits.MaxRequestBodySize = maxRequestBodySize;

                // 在 Response 中不包含 Server 属性。
                options.AddServerHeader = false;
            });

            services.Configure<IISServerOptions>(options =>
            {
                // 同步 IO。
                options.AllowSynchronousIO = true;

                // 限制请求大小。
                options.MaxRequestBodySize = maxRequestBodySize;
            });

            if (UseCompression)
            {
                services.AddResponseCompression(options =>
                {
                    options.EnableForHttps = true;
                    options.Providers.Add<BrotliCompressionProvider>();
                    options.Providers.Add<GzipCompressionProvider>();
                });
            }

            services.AddControllers();
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        }

        /// <remarks>此方法由运行时调用。</remarks>
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment()) app.UseDeveloperExceptionPage();

            _usedWebSocket = UseWebSocket;
            if (_usedWebSocket)
            {
                var wsOptions = new WebSocketOptions();
                var keepAlive = KeepAlive;
                if (keepAlive > 0) wsOptions.KeepAliveInterval = TimeSpan.FromSeconds(keepAlive);
                app.UseWebSockets(wsOptions);
            }

            app.Run(Handler);
        }

        Task Handler(HttpContext context)
        {
            try
            {
                if (_usedWebSocket && UseWebSocket)
                {
                    if (context.WebSockets.IsWebSocketRequest)
                    {
                        using (var ws = context.WebSockets.AcceptWebSocketAsync())
                        {
                            OnWebSocket(context, ws.Result);
                        }
                        return Task.CompletedTask;
                    }
                }

                OnContext(context);
            }
            catch { }
            return Task.CompletedTask;
        }

        #endregion

    }

}

#endif
